/*
 * Copyright 2010 (c) Dou Yongwang (douyongwang@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */      


#ifndef SJSON_H___   
#define SJSON_H___   

// INCLUDES
#include <e32std.h>
#include <e32base.h>
#include <S32STRM.H>
#include <e32hashtab.h>

namespace sjson {

class  CJSONValue;


_LIT8(KDoubleQuote, "\"");
_LIT8(KSingleQuote, "\'");

#if 0
class CJSONBase
	{
public:
	inline CJSONBase()	{}
//	IMPORT_C virtual ~CJSONBase();
//	inline TAny* operator new(TUint aSize, TAny* aBase) __NO_THROW;
//	inline TAny* operator new(TUint aSize) __NO_THROW;
//	inline TAny* operator new(TUint aSize, TLeave);
//	inline TAny* operator new(TUint aSize, TUint aExtraSize) __NO_THROW;
//	inline TAny* operator new(TUint aSize, TLeave, TUint aExtraSize);
//	IMPORT_C static void Delete(CBase* aPtr);

	TAny* operator new(TUint aSize) __NO_THROW {return User::AllocZ(aSize);}
	TAny* operator new(TUint aSize, TLeave) {return User::AllocZL(aSize);}
//	void operator delete(TAny* aPtr) {return User::Free(aPtr);}
protected:
	/*virutal*/ ~CJSONBase() {}
	};
#else
typedef CBase CJSONBase;
#endif

#if 0
//class CJSONObject;
//class CJSONValue;
//class CJSONBase;

class CJSONBase : public CObject
	{
	
	};
#endif


// JSON Object
class CJSONObject : public CJSONBase
	{
public:
	static CJSONObject* NewL();
    ~CJSONObject();
    
public:
    TInt Parse(const TDesC8& aInput, const TDesC8& aQuoteChar = KDoubleQuote);
    TInt Add(const TDesC8& aKey, const TDesC8& aVal);
//    TInt Add(const TDesC8& aKey, TInt32 aVal);
    TInt Add(const TDesC8& aKey, TInt64 aVal);
    TInt Add(const TDesC8& aKey, TBool aVal);
    TInt Add(const TDesC8& aKey, CJSONValue*& aVal);
    TInt Count();
    CJSONValue* GetL(const TDesC8& aKey);

    template <typename T>
    TBool Has(const TDesC8& aKey);

    // Always call has<>() first. If the key doesn't exist, consider
    // the behavior undefined.
    template <typename T>
    T& Get(const TDesC8& aKey);

    HBufC8* ToJSONStringLC(const TDesC8& aQuoteChar = KDoubleQuote) const;

    TBool NextKey();
    const TDesC8& CurrentKey() const;
    void Reset();

//    void ExternalizeL(RWriteStream &aStream) const;
//	void InternalizeL(RReadStream& aStream);

  private:
	CJSONObject();
    CJSONObject(const CJSONObject&);
    CJSONObject& operator=(const CJSONObject&);
    void CleanMap();

    typedef RPtrHashMap<TDesC8, CJSONValue> RValueMap;
    typedef TPtrHashMapIter<TDesC8, CJSONValue> RValueMapIter;
//    typedef RPtrHashMap<HBufC8, CJSONValue> RValueMap;
//    typedef RHashMap<TDesC8, CJSONValue*> RValueMap;
    RValueMap iValueMap;
#if 0
//    RValueMap::TIter iIter;
#else
    RValueMapIter iIter;
#endif
};

// JSON Array
class CJSONArray : public CJSONBase
	{
public:
	static CJSONArray* NewL();

  public:
    ~CJSONArray();
    TInt Parse(const TDesC8& aInput, const TDesC8& aQuoteChar = KDoubleQuote);

    TUint Size() { return iValues.Count(); }
    CJSONValue& operator [](TUint i) { return *(iValues[i]); }
    template <typename T>
    TBool Has(TUint i);

    template <typename T>
    T& Get(TUint i);

    HBufC8* ToJSONStringLC(const TDesC8& aQuoteChar = KDoubleQuote) const;
//    void ExternalizeL(RWriteStream &aStream) const;
//	void InternalizeL(RReadStream& aStream);

  private:
	CJSONArray();
    CJSONArray(const CJSONArray&);
    CJSONArray& operator=(const CJSONArray&);

    typedef RPointerArray<CJSONValue> RValueArray;
    RValueArray iValues;
};



// A value could be a number, an array, a string, an object, a
// boolean, or null
class CJSONValue : public CJSONBase
	{
public:
    static CJSONValue* NewL();
//    static CJSONValue* NewL(TInt32 aIntValue);
    static CJSONValue* NewL(TInt64 aInt64Value);
    static CJSONValue* NewL(HBufC8* aTDesC8Value);		// ownership transferred
    static CJSONValue* NewL(TBool aBoolValue);
    static CJSONValue* NewL(CJSONArray* aArrValue);			// ownership transferred
    static CJSONValue* NewL(CJSONObject* aObCJSONValue);		// ownership transferred
  public:
    class Null {};

    ~CJSONValue();
    TInt Parse(const TDesC8& aInput, const TDesC8& aQuoteChar = KDoubleQuote);
    template<typename T>
    TBool Is();
    template<typename T>
    T& Get();
    
    inline TBool IsRawData() { return iType == ERawData; } 
    inline TPtrC8 RawData() { return *iRawData; }

    HBufC8* ToJSONStringLC(const TDesC8& aQuoteChar = KDoubleQuote) const;

//  void ExternalizeL(RWriteStream &aStream) const;
//	void InternalizeL(RReadStream& aStream);

  private:
	CJSONValue();
    CJSONValue(const CJSONValue&);
    explicit CJSONValue(TInt64 aInt64Value);
    explicit CJSONValue(HBufC8* aTDesC8Value);		// ownership transferred
    explicit CJSONValue(TBool aBoolValue);
    explicit CJSONValue(CJSONArray* aArrValue);			// ownership transferred
    explicit CJSONValue(CJSONObject* aObCJSONValue);		// ownership transferred
    CJSONValue& operator=(const CJSONValue&);
    void reset();
    enum TType {
		EInvalid = -1,
		ENull = 0,
//        EInteger,
        EInt64,
        EString,
        EBool,
        EArray,
        EObject,
        ERawData
    } iType;
    union {
//		TInt32 iIntegerValue;
		TInt64 iInt64Value;
        HBufC8* iStringValue;
        TBool iBoolValue;
        CJSONArray* iArrayValue;
        CJSONObject* iObjectValue;
        HBufC8* iRawData;
    };
};



template <typename T>
TBool CJSONArray::Has(TUint i) {
    if (i >= Size()) {
        return false;
    } else {
        return iValues[i]->Is<T>();
    }
}

template <typename T>
T& CJSONArray::Get(TUint i) {
    ASSERT(i < Size());
    return iValues[i]->Get<T>();
}


//inline CJSONValue::CJSONValue(TInt32 aIntValue)
//	: iType(EInteger), iIntegerValue(aIntValue) {}
inline CJSONValue::CJSONValue(TInt64 aInt64Value)
	: iType(EInt64), iInt64Value(aInt64Value) {}
inline CJSONValue::CJSONValue(HBufC8* aTDesC8Value)
	: iType(EString), iStringValue(aTDesC8Value) {}
inline CJSONValue::CJSONValue(TBool aBoolValue)
	: iType(EBool), iBoolValue(aBoolValue) {}
inline CJSONValue::CJSONValue(CJSONArray* aArrValue)
	: iType(EArray), iArrayValue(aArrValue)	{}
inline CJSONValue::CJSONValue(CJSONObject* aObCJSONValue)
	: iType(EObject), iObjectValue(aObCJSONValue) {}


template <typename T>
TBool CJSONObject::Has(const TDesC8& aKey)
	{
	CJSONValue* pValue = iValueMap.Find(aKey);
	if (pValue)
		return (pValue)->Is<T>();
	return EFalse;
	}

template <typename T>
T& CJSONObject::Get(const TDesC8& aKey)
	{
    ASSERT(Has<T>(aKey));
	return iValueMap.Find(aKey)->Get<T>();
	}

template<>
inline TBool CJSONValue::Is<CJSONValue::Null>() {
    return iType == ENull;
}

template<>
inline TBool CJSONValue::Is<TBool>() {
    return iType == EBool;
}

template<>
inline TBool CJSONValue::Is<TDesC8>() {
    return iType == EString;
}

//template<>
//inline TBool CJSONValue::Is<TInt32>() {
//    return iType == EInteger;
//}

template<>
inline TBool CJSONValue::Is<TInt64>() {
    return iType == EInt64;
}

template<>
inline TBool CJSONValue::Is<CJSONArray>() {
    return iType == EArray;
}

template<>
inline TBool CJSONValue::Is<CJSONObject>() {
    return iType == EObject;
}

template<>
inline TBool& CJSONValue::Get<TBool>() {
    ASSERT(Is<TBool>());
    return iBoolValue;
}

template<>
inline TDesC8& CJSONValue::Get<TDesC8>() {
    ASSERT(Is<TDesC8>());
    return *iStringValue;
}

//template<>
//inline TInt32& CJSONValue::Get<TInt32>() {
//    ASSERT(Is<TInt32>());
//    return iIntegerValue;
//}

template<>
inline TInt64& CJSONValue::Get<TInt64>() {
    ASSERT(Is<TInt64>());
    return iInt64Value;
}

template<>
inline CJSONArray& CJSONValue::Get<CJSONArray>() {
    ASSERT(Is<CJSONArray>());
    return *iArrayValue;
}

template<>
inline CJSONObject& CJSONValue::Get<CJSONObject>() {
    ASSERT(Is<CJSONObject>());
    return *iObjectValue;
}


void Dump(const CJSONValue& value);
HBufC8* DecodeJSONString(const TDesC8& aData);
HBufC8* DecodeJSONString1(const TDesC8& aData);

HBufC8* EncodeJSONString(const TDesC8& aData, TBool aEnclose=ETrue);
HBufC8* EncodeJSONString1(const TDesC8& aData, TBool aEnclose=ETrue);
} // sjson

#endif // SJSON_H
